/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.engine.runtime.datalist;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.EntityNotFoundException;
import cn.easyplatform.ScriptRuntimeException;
import cn.easyplatform.contexts.ListContext;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.contexts.WorkflowContext;
import cn.easyplatform.dao.BizDao;
import cn.easyplatform.dao.Page;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.Record;
import cn.easyplatform.engine.runtime.DataListProcess;
import cn.easyplatform.entities.beans.list.*;
import cn.easyplatform.entities.beans.table.TableBean;
import cn.easyplatform.entities.beans.table.TableField;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Lang;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.vos.ListInitVo;
import cn.easyplatform.messages.vos.datalist.*;
import cn.easyplatform.support.scripting.CompliableScriptEngine;
import cn.easyplatform.support.scripting.ScriptEngineFactory;
import cn.easyplatform.support.sql.SqlParser;
import cn.easyplatform.type.*;
import cn.easyplatform.util.MessageUtils;
import cn.easyplatform.util.RuntimeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
abstract class AbstractDataListProcess implements DataListProcess {

    private static final Logger log = LoggerFactory.getLogger(AbstractDataListProcess.class);

    protected boolean isPreCmd;

    @Override
    public ListVo init(CommandContext cc, ListBean bean, ListInitVo iv) {
        WorkflowContext ctx = cc.getWorkflowContext();
        isPreCmd = ctx.getParameterAsString("759").equals("Prev");
        if (isPreCmd && !iv.getType().equals(Constants.ACTIONBOX)
                && !iv.getType().equals(Constants.REPORT))
            return doPrev(cc, ctx, iv.getId());
        boolean isAuto = Strings.isBlank(bean.getQuery());
        boolean isDetailType = iv.getType().equalsIgnoreCase(Constants.DETAIL);
        TableBean table = null;
        if (!Strings.isBlank(bean.getTable())) {
            if (bean.getTable().startsWith("$")) {
                String tableId = (String) ctx.getRecord().getValue(bean.getTable().substring(1));
                table = cc.getEntity(tableId);
                bean = bean.clone();
                bean.setTable(tableId);
            } else
                table = cc.getEntity(bean.getTable());
            if (table == null)
                throw new EntityNotFoundException(EntityType.TABLE.getName(),
                        bean.getTable());
        } else if (isDetailType || isAuto)// report类型可以不用指向具体表
            throw new EasyPlatformWithLabelKeyException(
                    "datalist.table.not.set", bean.getId());

        ListContext lc = ctx.setList(cc, iv, bean);
        if (!Strings.isBlank(lc.getInit())) {
            String code = RuntimeUtils.eval(cc, lc.getInit(),
                    ctx.getRecord());
            if (!code.equals("0000"))
                throw new ScriptRuntimeException(code, cc.getMessage(
                        code, ctx.getRecord()));
        }

        StringBuilder sb = new StringBuilder();

        List<ListHeaderVo> hvs = new ArrayList<ListHeaderVo>();

        List<ListAuxHeadVo> ahvs = new ArrayList<ListAuxHeadVo>();

        List<ListGroupVo> groups = new ArrayList<ListGroupVo>();

        List<String> keys = null;
        if (isDetailType)
            sb.append("select * ");
        else if (table != null) {
            keys = new ArrayList<String>();
            if (table.getKey() != null)
                keys.addAll(table.getKey());
            if (isAuto)
                sb.append("select ");
        }
        Group group = null;
        if (!Strings.isBlank(iv.getGroup())
                || iv.getType().equals(Constants.REPORT)) {
            if (bean.getGroups() != null) {
                for (Group g : bean.getGroups()) {
                    if (iv.getType().equals(Constants.REPORT)) {
                        groups.add(createGroup(cc, g, null));
                        if (g.getName().equals(iv.getGroup()))
                            group = g;
                    } else if (g.getName().equals(iv.getGroup())) {
                        group = g;
                        groups.add(createGroup(cc, group, null));
                        break;
                    }
                }
            }
            if (group == null) {
                if (iv.getType().equals(Constants.REPORT)) {
                    if (bean.getGroups() != null && !bean.getGroups().isEmpty())
                        group = bean.getGroups().get(0);
                } else
                    throw new EasyPlatformWithLabelKeyException(
                            "datalist.group.not.found", iv.getId(),
                            iv.getGroup());

            }
        }
        for (Header header : bean.getHeaders()) {
            ListHeaderVo hv = createHeader(cc, ctx.getRecord(), bean, table,
                    keys, header, isAuto && !isDetailType ? sb : null);
            hvs.add(hv);
        }
        if (bean.getAuxHeads() != null) {
            for (AuxHead head : bean.getAuxHeads()) {
                ListAuxHeadVo av = new ListAuxHeadVo();
                av.setStyle(head.getStyle());
                for (AuxHeader ah : head.getAuxHeaders()) {
                    ListAuxHeaderVo ahv = new ListAuxHeaderVo();
                    ahv.setStyle(ah.getStyle());
                    ahv.setColspan(ah.getColspan());
                    ahv.setRowspan(ah.getRowspan());
                    ahv.setAlign(ah.getAlign());
                    ahv.setHoverimg(ah.getHoverimg());
                    ahv.setIconSclass(ah.getIconSclass());
                    ahv.setImage(ah.getImage());
                    ahv.setValign(ah.getValign());
                    ahv.setTitle(RuntimeUtils.getLabel(cc, ah.getTitle()));
                    av.addAuxHeader(ahv);
                }
                ahvs.add(av);
            }
        }
        RecordContext source = null;
        if (iv instanceof ListListInitVo) {// actionbox
            ListListInitVo liv = (ListListInitVo) iv;
            ListContext list = ctx.getList(liv.getSid());
            source = list.getRecord(liv.getKeys());
            if (source == null)
                source = list.createRecord(liv.getKeys());
        } else
            source = ctx.getRecord();

        boolean isCustom = false;

        if (!lc.isFromInit()) {
            if (isDetailType)
                sb.append(" from ").append(table.getId());
            else if (isAuto) {
                if (keys.isEmpty()) {// 表示都已包含列表定义中
                    sb.deleteCharAt(sb.length() - 1);// 删除最后一个逗号
                } else {// 添加余下未在列表中的主键栏位
                    Iterator<String> itr = keys.iterator();
                    while (itr.hasNext()) {
                        sb.append(itr.next());
                        if (itr.hasNext())
                            sb.append(",");
                        itr.remove();
                    }
                }
                sb.append(" from ").append(table.getId());
                isCustom = iv.isUseQueryResult();
            } else {
                if (keys == null || !keys.isEmpty())// key为空或者KEY在列表中不完整
                    isCustom = true;
                else
                    isCustom = iv.isUseQueryResult();
                SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
                String query = sp.parse(bean.getQuery().trim(), source);
                lc.setParams(sp.getParams());
                sb.append(query);
            }
            String resultSql = sb.toString();
            // 保存列表信息
            lc.setResultSql(resultSql);
            lc.setCustom(isCustom);
            // 控件的排序定义具有优先级
            if (!Strings.isBlank(iv.getOrderBy()))
                lc.setOrderBy(iv.getOrderBy());
            else if (group != null && !Strings.isBlank(group.getOrderBy()))
                lc.setOrderBy(group.getOrderBy());
            // 控件上的条件具有优先级
            if (iv.getCondition() != null)
                lc.setCondition(iv.getCondition());
            else
                lc.setCondition(bean.getCondition());
        }
        lc.setHeaders(hvs);
        lc.setAuxHeads(ahvs);
        lc.setGroups(groups);
        if (keys != null && keys.isEmpty() || isDetailType)
            lc.setKeyFields(table.getKey());
        // 返回对象
        ListVo lv = new ListVo(lc.getBean().getName());
        lv.setHeaders(hvs);
        lv.setAuxHeads(ahvs);
        lv.setGroups(groups);
        lv.setOnRow(bean.getOnRowScript());
        lv.setOnHeader(bean.getOnHeaderScript());
        lv.setOnGroup(bean.getOnGroupScript());
        lv.setOnFooter(bean.getOnFooterScript());
        lv.setRowStyle(bean.getRowStyle());
        lv.setHeadStyle(bean.getHeadStyle());
        lv.setFootStyle(bean.getFootStyle());
        lv.setMatrixColumns(bean.getMatrixColumns());
        lv.setMatrixRows(bean.getMatrixRows());
        // 表名称为空且是自定义查询
        lv.setCustom(lc.isCustom());
        if (iv.isShowPanel() && !Strings.isBlank(bean.getPanel()))
            lv.setPanel(DataListUtils.parsePanel(cc, ctx.getRecord(),
                    bean.getPanel()));
        if (iv.isImmediate()) {
            if (lc.isFromInit()) {
                lc.setStartPageNo(1);
                List<RecordContext> data = lc.getRecords();
                if (data != null) {
                    isPreCmd = true;
                    lv.setTotalSize(data.size());
                    lv.setData(createRows(cc, lc, ctx.getRecord(), data));
                }
            } else {
                lc.clear(0);
                List<FieldDo> params = null;
                if (Strings.isBlank(iv.getHost())
                        && !Strings.isBlank(lc.getCondition())) {
                    SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
                    sb.append(" where ").append(
                            sp.parse(lc.getCondition(), source));
                    params = sp.getParams();
                }
                lc.setSql(sb.toString());
                if (params != null) {
                    if (lc.getParams() != null)
                        lc.getParams().addAll(params);
                    else
                        lc.setParams(params);
                }
                params = lc.getParams();
                if (!Strings.isBlank(lc.getGroupBy())) {
                    sb.append(" group by ");
                    String groupBy = lc.getGroupBy().trim();
                    if (groupBy.startsWith("$"))
                        groupBy = (String) ctx.getRecord().getValue(
                                groupBy.substring(1));
                    sb.append(groupBy);
                }
                if (log.isInfoEnabled()) {
                    log.info("sql->{},{}", sb, lc.getOrderBy());
                    log.info("params->{}", params);
                }
                BizDao dao = null;
                if (table == null)
                    dao = cc.getBizDao();
                else
                    dao = cc.getBizDao(table.getSubType());
                List<FieldDo[]> result = null;
                int listCount;
                lc.setStartPageNo(1);
                if (iv.getPageSize() > 0 && !isDetailType
                        && !iv.getType().equals(Constants.REPORT)) {
                    Page page = new Page(iv.getPageSize());
                    page.setPageNo(iv.getStartPageNo());
                    if (!Strings.isBlank(lc.getOrderBy())) {
                        String orderBy = lc.getOrderBy().trim();
                        if (orderBy.startsWith("$"))
                            orderBy = (String) ctx.getRecord().getValue(
                                    orderBy.substring(1));
                        page.setOrderBy(orderBy);
                    }
                    result = dao.selectList(sb.toString(), params, page);
                    listCount = page.getTotalCount();
                } else {
                    if (!Strings.isBlank(lc.getOrderBy())) {
                        sb.append(" order by ");
                        String orderBy = lc.getOrderBy().trim();
                        if (orderBy.startsWith("$"))
                            orderBy = (String) ctx.getRecord().getValue(
                                    orderBy.substring(1));
                        sb.append(orderBy);
                    }
                    //使用session缓存
                    if (iv.isCache())
                        result = (List<FieldDo[]>) cc.get(bean.getId());
                    if (result == null) {
                        result = dao.selectList(sb.toString(), params);
                        if (iv.isCache())
                            cc.set(bean.getId(), result);
                    }
                    listCount = result.size();
                }
                try {
                    lv.setTotalSize(listCount);
                    lv.setData(createRows(cc, lc, ctx.getRecord(), result));
                } catch (Exception ex) {
                    if (log.isErrorEnabled())
                        log.error("init", ex);
                    ctx.removeList(bean.getId());
                    throw Lang.wrapThrow(ex);
                }
            }
        } else {
            lc.setSql(sb.toString());
        }
        return lv;
    }

    private ListGroupVo createGroup(CommandContext cc, Group g,
                                    ListGroupVo parent) {
        ListGroupVo group = new ListGroupVo();
        group.setFields(g.getFields().split(","));
        group.setName(g.getName());
        group.setTitle(RuntimeUtils.getLabel(cc, g.getTitle()));
        group.setStyle(g.getStyle());
        if (parent != null)
            parent.addGroup(group);
        if (g.getChild() != null)
            createGroup(cc, g.getChild(), group);
        return group;
    }

    private ListVo doPrev(CommandContext cc, WorkflowContext ctx, String id) {
        ListContext lc = ctx.getList(id);
        ListRecurVo lv = new ListRecurVo(lc.getBean().getName());
        lv.setHeaders(lc.getHeaders());
        lv.setAuxHeads(lc.getAuxHeads());
        lv.setGroups(lc.getGroups());
        lv.setCustom(lc.isCustom());
        lv.setOnRow(lc.getBean().getOnRowScript());
        lv.setOnHeader(lc.getBean().getOnHeaderScript());
        lv.setOnGroup(lc.getBean().getOnGroupScript());
        lv.setOnFooter(lc.getBean().getOnFooterScript());
        if (!Strings.isBlank(lc.getBean().getPanel()))
            lv.setPanel(DataListUtils.parsePanel(cc, ctx.getRecord(), lc
                    .getBean().getPanel()));
        lv.setPanelQueryInfo(lc.getPanelQueryInfo());
        if (lc.getStartPageNo() >= 0) {
            lv.setData(loadData(cc, lc));
            lv.setStartPageNo(lc.getStartPageNo());
            lv.setTotalSize(lc.getTotalSize());
        }
        return lv;
    }

    @Override
    public ListQueryResultVo reload(CommandContext cc, ListContext lc,
                                    RecordContext from) {
        if (!Strings.isBlank(lc.getHost()))
            lc.setHostKey(from.getKeyValues());
        if ((lc.isInited() && lc.getType().equalsIgnoreCase(Constants.DETAIL))) {// 从内存中加载
            isPreCmd = true;
            List<ListRowVo> data = loadData(cc, lc);
            return new ListQueryResultVo(data, data.size());
        }
        lc.clear(0);
        if (lc.isFromInit()) {
            String code = RuntimeUtils.eval(cc, lc.getInit(),
                    from);
            if (!code.equals("0000"))
                throw new ScriptRuntimeException(code, cc.getMessage(
                        code, from));
            List<ListRowVo> data = DataListUtils.loadData(cc, lc);
            return new ListQueryResultVo(data, data.size());
        }
        if (log.isInfoEnabled())
            log.info("reload->from[{}],key[{}]", lc.getHost(),
                    Lang.concat(from.getKeyValues()));
        StringBuilder sb = new StringBuilder();
        List<FieldDo> params = new ArrayList<FieldDo>();
        if (!Strings.isBlank(lc.getBean().getQuery())
                && !Strings.isBlank(lc.getCondition())) {
            SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
            String query = sp.parse(lc.getBean().getQuery().trim(), from);
            List<FieldDo> tmp = sp.getParams();
            sb.append(query);
            sp = RuntimeUtils.createSqlParser(FieldDo.class);
            sb.append(" where ").append(sp.parse(lc.getCondition(), from));
            tmp.addAll(sp.getParams());
            lc.setSql(sb.toString());
            lc.setParams(tmp);
            params.addAll(tmp);
            if (!Strings.isBlank(lc.getPanelQuery())) {
                sb.append(lc.getPanelQuery());
                if (lc.getPanelParams() != null)
                    params.addAll(lc.getPanelParams());
            }
        } else if (!Strings.isBlank(lc.getCondition())) {
            sb.append(lc.getResultSql());
            SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
            sb.append(" where ").append(sp.parse(lc.getCondition(), from));
            lc.setSql(sb.toString());
            lc.setParams(sp.getParams());
            params.addAll(lc.getParams());
            if (!Strings.isBlank(lc.getPanelQuery())) {
                sb.append(lc.getPanelQuery());
                if (lc.getPanelParams() != null)
                    params.addAll(lc.getPanelParams());
            }
        } else if (!Strings.isBlank(lc.getBean().getQuery())) {
            SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
            sb.append(sp.parse(lc.getResultSql(), from));
            lc.setSql(sb.toString());
            lc.setParams(sp.getParams());
            params.addAll(lc.getParams());
            if (!Strings.isBlank(lc.getPanelQuery())) {
                sb.append(lc.getPanelQuery());
                if (lc.getPanelParams() != null)
                    params.addAll(lc.getPanelParams());
            }
        } else {
            sb.append(lc.getResultSql());
            lc.setSql(sb.toString());
            if (!Strings.isBlank(lc.getPanelQuery())) {
                sb.append(" ").append(lc.getPanelQuery());
                params = lc.getPanelParams();
            }
        }
        if (!Strings.isBlank(lc.getGroupBy())) {
            sb.append(" group by ");
            String groupBy = lc.getGroupBy().trim();
            if (groupBy.startsWith("$"))
                groupBy = (String) from.getValue(groupBy.substring(1));
            sb.append(groupBy);
        }
        if (log.isInfoEnabled()) {
            log.info("sql->{}", sb);
            log.info("params->{}", params);
        }
        lc.setStartPageNo(1);
        BizDao dao = null;
        if (Strings.isBlank(lc.getBean().getTable()))
            dao = cc.getBizDao();
        else {
            TableBean tb = cc.getEntity(lc.getBean().getTable());
            dao = cc.getBizDao(tb.getSubType());
        }
        lc.setStartPageNo(1);
        List<FieldDo[]> result = null;
        int total = 0;
        if (lc.getPageSize() > 0 && !lc.getType().equals(Constants.DETAIL)
                && !lc.getType().equals(Constants.REPORT)) {
            Page page = new Page(lc.getPageSize());
            page.setPageNo(1);
            if (!Strings.isBlank(lc.getOrderBy())) {
                String orderBy = lc.getOrderBy().trim();
                if (orderBy.startsWith("$"))
                    orderBy = (String) from.getValue(orderBy.substring(1));
                page.setOrderBy(orderBy);
            }
            result = dao.selectList(sb.toString(), params, page);
            total = page.getTotalCount();
        } else {
            if (!Strings.isBlank(lc.getOrderBy())) {
                sb.append(" order by ");
                String orderBy = lc.getOrderBy().trim();
                if (orderBy.startsWith("$"))
                    orderBy = (String) from.getValue(orderBy.substring(1));
                sb.append(orderBy);
            }
            result = dao.selectList(sb.toString(), params);
            total = result.size();
        }
        lc.setTotalSize(total);
        List<ListRowVo> data = createRows(cc, lc, from, result);
        return new ListQueryResultVo(data, total);
    }

    protected <T> List<ListRowVo> createRows(CommandContext cc, ListContext lc,
                                             RecordContext from, List<T> result) {
        List<ListRowVo> list = new ArrayList<ListRowVo>();
        if (!result.isEmpty()) {
            CompliableScriptEngine onRowEngine = null;
            CompliableScriptEngine filterEngine = null;
            try {
                if (!Strings.isBlank(lc.getOnRow()))
                    onRowEngine = ScriptEngineFactory.createCompilableEngine(
                            cc, lc.getOnRow());
                if (!Strings.isBlank(lc.getFilter())
                        && result.get(0) instanceof FieldDo[])
                    filterEngine = ScriptEngineFactory.createCompilableEngine(
                            cc, lc.getFilter());
                for (T data : result) {
                    if (data instanceof FieldDo[]) {// 从数据库顺获取
                        ListRowVo row = createRow(cc, lc, onRowEngine,
                                filterEngine, from, (FieldDo[]) data);
                        if (row != null)
                            list.add(row);
                    } else {
                        // 从内存中
                        RecordContext rc = (RecordContext) data;
                        list.add(createRow(cc, lc, onRowEngine, from, rc));
                    }
                }
            } finally {
                if (onRowEngine != null) {
                    onRowEngine.destroy();
                    onRowEngine = null;
                }
                if (filterEngine != null) {
                    filterEngine.destroy();
                    filterEngine = null;
                }
            }
        }
        return list;
    }

    /**
     * @param cc
     * @param lc
     * @param onRowEngine
     * @param from
     * @param rc
     * @return
     */
    private ListRowVo createRow(CommandContext cc, ListContext lc,
                                CompliableScriptEngine onRowEngine, RecordContext from,
                                RecordContext rc) {
        // 除报表类型外，其它都要有主键值
        Object[] keys = null;
        if (lc.getKeyFields() != null) {
            keys = new Object[lc.getKeyFields().size()];
            int index = 0;
            for (String f : lc.getKeyFields())
                keys[index++] = rc.getValue(f);
        }
        // 初始化每笔数据的非栏位变量
        List<ListHeaderVo> headers = lc.getHeaders();
        int size = headers.size();
        for (int i = 0; i < size; i++) {
            ListHeaderVo hv = headers.get(i);
            if (Strings.isBlank(hv.getField()) && hv.getType() != null) {
                FieldDo var = new FieldDo(hv.getType());
                var.setName(hv.getName());
                var.setScope(ScopeType.PRIVATE);
                if (hv.getType() == FieldType.NUMERIC) {
                    var.setDecimal(2);
                    var.setLength(19);
                }
                rc.setVariable(var);
            }
        }
        if (onRowEngine != null)
            onRowEngine.eval(from, rc);
        Object[] data = DataListUtils.wrapRow(cc, lc, rc);
        return new ListRowVo(keys, data, rc.getParameterAsBoolean("853"));
    }

    /**
     * @param cc
     * @param lc
     * @param onRowEngine
     * @param filterEngine
     * @param from
     * @param fieldValues
     * @return
     */
    private ListRowVo createRow(CommandContext cc, ListContext lc,
                                CompliableScriptEngine onRowEngine,
                                CompliableScriptEngine filterEngine, RecordContext from,
                                FieldDo[] fieldValues) {
        // 除报表类型外，其它都要有主键值
        Object[] keys = null;
        if (lc.getKeyFields() != null)
            keys = new Object[lc.getKeyFields().size()];
        Record record = new Record();
        // 先预设值
        for (int i = 0; i < fieldValues.length; i++) {
            record.set(fieldValues[i]);
            if (keys != null) {
                for (String f : lc.getKeyFields()) {
                    if (f.toUpperCase().equals(fieldValues[i].getName())) {
                        keys[lc.getKeyFields().indexOf(f)] = fieldValues[i]
                                .getValue();
                        break;
                    }
                }
            }
        }
        RecordContext rc = lc.createRecord(keys, record);
        // 初始化每笔数据的非栏位变量
        List<ListHeaderVo> headers = lc.getHeaders();
        int size = headers.size();
        for (int i = 0; i < size; i++) {
            ListHeaderVo hv = headers.get(i);
            if (Strings.isBlank(hv.getField()) && hv.getType() != null) {
                FieldDo var = new FieldDo(hv.getType());
                var.setName(hv.getName());
                var.setScope(ScopeType.PRIVATE);
                if (hv.getType() == FieldType.NUMERIC) {
                    var.setDecimal(2);
                    var.setLength(19);
                }
                rc.setVariable(var);
            }
        }
        if (filterEngine != null) {
            Object obj = filterEngine.eval(from, rc);
            // 返回true表示过滤掉
            if (obj != null && obj instanceof Boolean && (Boolean) obj)
                return null;
        }
        if (onRowEngine != null)
            onRowEngine.eval(from, rc);
        Object[] data = DataListUtils.wrapRow(cc, lc, rc);
        fieldValues = null;
        if ((!isPreCmd && lc.getType().equals(Constants.DETAIL))) {
            rc.setParameter("814", "R");
            rc.setParameter("815", false);
            lc.appendRecord(rc);
        }
        return new ListRowVo(keys, data, rc.getParameterAsBoolean("853"));
    }

    protected ListHeaderVo createHeader(CommandContext cc, RecordContext rc,
                                        ListBean bean, TableBean table, List<String> keys, Header header, StringBuilder sb) {
        ListHeaderVo hv = new ListHeaderVo();
        hv.setName(header.getName());
        hv.setDraggable(header.getDraggable());
        hv.setCellStyle(header.getCellStyle());
        if (!Strings.isBlank(header.getComponent())) {
            String zul = header.getComponent();
            if (zul.startsWith("$"))
                zul = (String) rc.getValue(header.getComponent().substring(1));
            if (!zul.trim().startsWith("<idspace"))
                hv.setComponent(DataListUtils.parse(cc, rc, zul));
            else
                hv.setComponent(zul);
        }
        hv.setField(header.getField());
        if (Strings.isBlank(header.getFormat())) {
            if (table != null) {
                for (TableField tf : table.getFields()) {
                    if (tf.getName().equalsIgnoreCase(header.getField())) {
                        if (header.getType() == FieldType.NUMERIC) {
                            if (Strings.isBlank(tf.getAcc())) {
                                if (tf.getDecimal() == 0)
                                    hv.setFormat("###,##0");
                                else
                                    hv.setFormat("###,##0." + StringUtils.repeat("0", tf.getDecimal()));
                            } else {
                                hv.setFormat("$" + tf.getAcc());
                            }
                        }
                        hv.setOptionType(Option.Type.valueOf(tf.getOptionType() == null ? 0 : tf.getOptionType()));
                        hv.setOptionValue(tf.getOptionValue());
                        break;
                    }
                }
            }
        } else
            hv.setFormat(header.getFormat());
        if (!Strings.isBlank(header.getTotalName()))
            hv.setTotalName(RuntimeUtils.getLabel(cc, rc, header.getTotalName()));
        hv.setTotalStyle(header.getTotalStyle());
        if (!Strings.isBlank(header.getGroupName()))
            hv.setGroupName(RuntimeUtils.getLabel(cc, rc, header.getGroupName()));
        hv.setGroupStyle(header.getGroupStyle());
        hv.setSort(header.isSort());
        hv.setStyle(header.getStyle());
        if (!Strings.isBlank(header.getTitle())) {
            if (header.getTitle().startsWith("$"))
                hv.setTitle(header.getTitle());
            else
                hv.setTitle(RuntimeUtils.getLabel(cc, rc, header.getTitle()));
        }
        hv.setTotalType(header.getTotalType());
        hv.setType(header.getType());
        hv.setVisible(header.isVisible());
        hv.setWidth(header.getWidth());
        hv.setEvent(header.getEvent());
        hv.setImage(header.getImage());
        hv.setAlign(header.getAlign());
        hv.setValign(header.getValign());
        hv.setIconSclass(header.getIconSclass());
        hv.setHoverimg(header.getHoverimg());
        hv.setSql(header.getSql());
        hv.setSqlSeparator(header.getSqlSeparator());
        if (!Strings.isBlank(hv.getField())) {
            if (hv.getType() == null)
                throw new EasyPlatformWithLabelKeyException(
                        "datalist.field.type.not.found", bean.getId(), hv.getField());
            if (sb != null)
                sb.append(hv.getField()).append(",");
            if (keys != null && keys.contains(hv.getField()))
                keys.remove(hv.getField());
            // 如果栏位不是空的，name就与栏位一致
//            hv.setName(hv.getField());
        }
        return hv;
    }

    @Override
    public Object load(CommandContext cc, ListContext lc, ListLoadVo from) {
        BizDao dao = null;
        if (Strings.isBlank(lc.getBean().getTable())) {
            dao = cc.getBizDao();
        } else {
            TableBean table = cc.getEntity(lc.getBean().getTable());
            dao = cc.getBizDao(table.getSubType());
        }
        WorkflowContext ctx = cc.getWorkflowContext();
        SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
        List<FieldDo> params = null;
        StringBuilder sb = new StringBuilder();
        if (from.getQuery().toUpperCase().startsWith("SELECT")) {
            sb.append(sp.parse(from.getQuery(), ctx.getRecord()));
            params = sp.getParams();
        } else {
            sb.append(lc.getResultSql());
            if (lc.isCustom()) {
                sb.append(" AND (").append(
                        sp.parse(from.getQuery(), ctx.getRecord())).append(")");
                params = new ArrayList<>(lc.getParams());
                params.addAll(sp.getParams());
            } else {
                sb.append(" where ").append(
                        sp.parse(from.getQuery(), ctx.getRecord()));
                params = sp.getParams();
            }
        }
        params.add(RuntimeUtils.castTo(from.getValue()));
        if (lc.getPanelParams() != null) {
            params.addAll(lc.getPanelParams());
            sb.append(lc.getPanelQuery());
        }
        if (from.isCount()) {
            return dao.getCount(sb.toString(), params);
        } else {
            if (!Strings.isBlank(lc.getOrderBy())) {
                sb.append(" order by ");
                String orderBy = lc.getOrderBy().trim();
                if (orderBy.startsWith("$"))
                    orderBy = (String) ctx.getRecord().getValue(
                            orderBy.substring(1));
                sb.append(orderBy);
            }
            List<FieldDo[]> result = dao.selectList(sb.toString(), params);
            return createRows(cc, lc, ctx.getRecord(), result);
        }
    }

    @Override
    public List<ListRowVo> paging(CommandContext cc, ListPagingVo pv) {
        WorkflowContext ctx = cc.getWorkflowContext();
        ListContext lc = ctx.getList(pv.getId());
        BizDao dao = null;
        if (Strings.isBlank(lc.getBean().getTable())) {
            dao = cc.getBizDao();
        } else {
            TableBean table = cc.getEntity(lc.getBean().getTable());
            dao = cc.getBizDao(table.getSubType());
        }
        Page page = null;
        int pz = pv.getPageSize() == 0 ? lc.getPageSize() : pv.getPageSize();
        if (pz > 0) {
            page = new Page(pz);
            page.setPageNo(pv.getPageNo());
            if (!Strings.isBlank(lc.getOrderBy())) {
                String orderBy = lc.getOrderBy().trim();
                if (orderBy.startsWith("$"))
                    orderBy = (String) ctx.getRecord().getValue(
                            orderBy.substring(1));
                page.setOrderBy(orderBy);
            }
            page.setGetTotal(false);
            lc.setStartPageNo(pv.getPageNo());
        }
        StringBuilder sb = new StringBuilder();
        sb.append(lc.getSql());
        List<FieldDo> params = new ArrayList<FieldDo>();
        if (lc.getParams() != null)
            params.addAll(lc.getParams());
        if (lc.getPanelParams() != null) {
            params.addAll(lc.getPanelParams());
            sb.append(lc.getPanelQuery());
        }
        if (!Strings.isBlank(lc.getGroupBy())) {
            sb.append(" group by ");
            String groupBy = lc.getGroupBy().trim();
            if (groupBy.startsWith("$"))
                groupBy = (String) ctx.getRecord().getValue(
                        groupBy.substring(1));
            sb.append(groupBy);
        }
        List<FieldDo[]> result = null;
        if (page == null) {
            if (!Strings.isBlank(lc.getOrderBy())) {
                sb.append(" order by ");
                String orderBy = lc.getOrderBy().trim();
                if (orderBy.startsWith("$"))
                    orderBy = (String) ctx.getRecord().getValue(
                            orderBy.substring(1));
                sb.append(orderBy);
            }
            result = dao.selectList(sb.toString(), params);
        } else
            result = dao.selectList(sb.toString(), params, page);
        params = null;
        sb = null;
        return createRows(cc, lc, ctx.getRecord(), result);
    }

    @Override
    public ListQueryResultVo query(CommandContext cc, ListQueryVo qv) {
        Iterator<ListQueryParameterVo> itr = qv.getParams().iterator();
        WorkflowContext ctx = cc.getWorkflowContext();
        ListContext lc = ctx.getList(qv.getId());
        List<FieldDo> params = new ArrayList<FieldDo>();
        StringBuilder sb = new StringBuilder();
        if (lc.getParams() == null) {
            if (!qv.getParams().isEmpty())
                sb.append(" where (");
        } else if (!qv.getParams().isEmpty())
            sb.append(" and (");
        while (itr.hasNext()) {
            ListQueryParameterVo pv = itr.next();
            sb.append(pv.getName()).append(" ");
            String op = pv.getOp();
            if (op != null
                    && (op.equals("is null") || op.equals("is not null"))) {
                sb.append(op);
            } else {
                Iterator<Object> ir = pv.getValues().iterator();
                Object v = ir.next();
                FieldType type = FieldType.cast(v);
                FieldDo fd = new FieldDo(type);
                fd.setValue(v);
                fd.setName(pv.getName());
                if (op == null) {
                    if (v instanceof String) {
                        op = "like";
                        params.add(fd);
                    } else if (v instanceof Object[]) {
                        op = "in";
                    } else {
                        op = "=";
                        params.add(fd);
                    }
                } else
                    params.add(fd);
                if (op.equals("ls")) {
                    fd.setValue(fd.getValue() + "%");
                    sb.append(" like ?");
                } else if (op.equals("lf")) {
                    fd.setValue("%" + fd.getValue());
                    sb.append(" like ?");
                } else {
                    sb.append(op);
                    if (op.equals("like") || op.equals("not like")) {
                        fd.setValue("%" + fd.getValue() + "%");
                        sb.append(" ?");
                    } else if (op.equals("between") || op.equals("not between")) {
                        sb.append(" ? and ?");
                        fd = new FieldDo(type);
                        fd.setValue(ir.next());
                        fd.setName(pv.getName());
                        params.add(fd);
                    } else if (op.equals("in") || op.equals("not in")) {
                        sb.append("(");
                        if (v instanceof Object[]) {
                            Object[] objs = (Object[]) fd.getValue();
                            for (int i = 0; i < objs.length; i++) {
                                FieldDo field = new FieldDo(
                                        FieldType.cast(objs[i]));
                                field.setName(fd.getName());
                                field.setValue(objs[i]);
                                sb.append("?");
                                if (i < objs.length - 1)
                                    sb.append(",");
                                params.add(field);
                            }
                        } else {
                            sb.append("?");
                            while (ir.hasNext()) {
                                sb.append(",?");
                                fd = new FieldDo(type);
                                fd.setValue(ir.next());
                                fd.setName(pv.getName());
                                params.add(fd);
                            }
                        }
                        sb.append(")");
                    } else
                        sb.append(" ?");
                }
            }
            if (itr.hasNext()) {
                if (pv.getRp() == null)
                    sb.append(" and ");
                else
                    sb.append(" ").append(pv.getRp()).append(" ");
            }
        }
        if (!qv.getParams().isEmpty())
            sb.append(")");
        // 保存查询信息
        lc.setPanelQuery(sb.toString());
        lc.setPanelParams(new ArrayList<FieldDo>(params));
        sb.insert(0, lc.getSql());
        if (lc.getParams() != null)
            params.addAll(0, lc.getParams());
        if (!Strings.isBlank(lc.getGroupBy())) {
            sb.append(" group by ");
            String groupBy = lc.getGroupBy().trim();
            if (groupBy.startsWith("$"))
                groupBy = (String) ctx.getRecord().getValue(
                        groupBy.substring(1));
            sb.append(groupBy);
        }
        if (log.isInfoEnabled()) {
            log.info("sql->{}", sb);
            log.info("params->{}", params);
        }
        lc.setStartPageNo(1);
        BizDao dao = null;
        if (Strings.isBlank(lc.getBean().getTable())) {
            dao = cc.getBizDao();
        } else {
            TableBean table = cc.getEntity(lc.getBean().getTable());
            dao = cc.getBizDao(table.getSubType());
        }
        List<FieldDo[]> result = null;
        int listCount;
        if (lc.getPageSize() > 0 && !lc.getType().equals(Constants.DETAIL)
                && !lc.getType().equals(Constants.REPORT)) {
            Page page = new Page(lc.getPageSize());
            page.setPageNo(1);
            if (!Strings.isBlank(lc.getOrderBy())) {
                String orderBy = lc.getOrderBy().trim();
                if (orderBy.startsWith("$"))
                    orderBy = (String) ctx.getRecord().getValue(
                            orderBy.substring(1));
                page.setOrderBy(orderBy);
            }
            result = dao.selectList(sb.toString(), params, page);
            listCount = page.getTotalCount();
        } else {
            if (!Strings.isBlank(lc.getOrderBy())) {
                sb.append(" order by ");
                String orderBy = lc.getOrderBy().trim();
                if (orderBy.startsWith("$"))
                    orderBy = (String) ctx.getRecord().getValue(
                            orderBy.substring(1));
                sb.append(orderBy);
            }
            result = dao.selectList(sb.toString(), params);
            listCount = result.size();
        }
        if (lc.getType().equals(Constants.DETAIL))
            lc.clear(0);
        lc.setTotalSize(listCount);
        List<ListRowVo> data = createRows(cc, lc, ctx.getRecord(), result);
        lc.setPanelQueryInfo(qv.getParams());
        return new ListQueryResultVo(data, listCount);
    }

    protected List<ListRowVo> loadData(CommandContext cc, ListContext lc) {
        return null;
    }
}
